#include <iostream>
#include <functional>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>

#include <netinet/in.h>
#include <arpa/inet.h>

#include <unistd.h>
#include <sys/wait.h>
#include <pthread.h>
#include "threadPool.hpp"
#include "task.hpp"

using namespace std;

class EchoServer;
class ThreadData
{
public:
    ThreadData(EchoServer* ts, int sock)
        : _this(ts)
        , _sock(sock)
    {}
public:
    EchoServer *_this;
    int _sock;
};

class EchoServer
{
public:
    using func_t = function<string(const string &)>;
    static const uint16_t default_port = 6666;
    static const int backlog = 32; // TODO
    EchoServer(func_t func, uint16_t port = default_port)
        : _port(port), _func(func)
    {
    }

    void init()
    {
        // 1.创建socket文件
        _listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);
        if (_listen_sockfd < 0)
        {
            cerr << "Create socket file description failed." << endl;
            exit(-1);
        }
        cout << "创建套接字成功，文件描述符: " << _listen_sockfd << endl;

        // 2.绑定[port, ip]
        struct sockaddr_in local;
        memset(&local, 0, sizeof(local));
        local.sin_family = AF_INET;
        local.sin_port = htons(_port);
        local.sin_addr.s_addr = htonl(INADDR_ANY); // 云服务器，绑定任意ip，INADDR_ANY就是全零0.0.0.0
        if (bind(_listen_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
        {
            cerr << "Bind socket failed." << endl;
            exit(-2);
        }
        cout << "绑定成功: [ip:" << inet_ntoa(local.sin_addr) << ", port:" << _port << "]" << endl;

        // 3.监听套接字
        if (listen(_listen_sockfd, backlog) == -1)
        {
            cerr << "Listen socket failed." << endl;
            exit(-3);
        }
        cout << "正在监听..." << endl;
    }

    void start()
    {
        // 防止父进程一直在阻塞wait的3个方案：
        // signal(SIGCHLD, SIG_IGN); // 1.推荐
        // signal(SIGCHLD, handler); // 2.不太推荐
        while (true)
        {
            // 4.建立连接，获得新的socket文件描述符
            struct sockaddr_in peer;
            socklen_t len = sizeof(peer);
            cout << "正在等待accept..." << endl;
            int sockfd = accept(_listen_sockfd, (struct sockaddr *)&(peer), &len);
            if (sockfd < 0)
            {
                cerr << "Accept socket failed." << endl;
                continue; // 继续去连接
            }
            cout << "连接成功: [ip:" << inet_ntoa(peer.sin_addr) << ", port:" << peer.sin_port << "]" << endl;

            // 5.连接成功，利用socket file description处理业务

            // 5.1 单进程版本：
            // service(sockfd);

            // 5.2 多进程版本：
            // pid_t id = fork();
            // if (id < 0)
            // {
            //     close(sockfd);
            //     exit(-1);
            // }
            // else if (id == 0) // 子进程，拷贝继承了父进程的fd table
            // {
            //     close(_listen_sockfd);
            //     if (fork() > 0) // 3.这里，马上让子进程退出，下面就是孙子进程变成孤儿在执行，由系统回收，无需wait
            //         exit(0);
            //     service(sockfd);
            //     exit(0);
            // }
            // // 父进程
            // close(sockfd);
            // pid_t ret = waitpid(id, nullptr, 0); // 它是阻塞式等待！得防止它一直在这阻塞导致无法再去accept，见上面3个方案
            // cout << "Wait success: " << ret << endl;

            // 5.3 多线程版本：
            // pthread_t tid;
            // pthread_create(&tid, nullptr, threadRun, new ThreadData(this, sockfd));

            // 5.4 线程池版本：
            // 注：线程池处理的应该是短任务，不应该是长任务甚至是死循环。所以那个service函数需要改改
            Task t(sockfd, std::bind(&EchoServer::service, this, std::placeholders::_1));
            xyl::ThreadPool<Task>::get_instance()->push_task(t);
        }
    }

private:
    static void *threadRun(void* args)
    {
        ThreadData *td = (ThreadData *)args;
        pthread_detach(pthread_self());
        td->_this->service(td->_sock);
        delete td;
        return nullptr;
    }

public:
    void service(int sockfd)
    {
        char buf[1024] = {0};
        while (true)
        {
            ssize_t n = read(sockfd, buf, sizeof(buf) - 1);
            if (0 == n) // 回忆一下管道的知识，read管道文件时，如果对方关闭管道文件了，继续读时会读到0个
            {
                close(sockfd);
                cout << "连接已关闭" << endl;
                break;
            }
            else if (0 < n) // 读取文件成功
            {
                buf[n] = '\0';
                cout << "收到一条消息：" << buf << endl;

                string res = _func(buf); // 回调
                // 发送回去 TODO
                write(sockfd, res.c_str(), res.size());
            }
            else // 读取失败
            {
                close(sockfd);
                cerr << "Read error" << endl;
                break;
            }
        }
    }

private:
    func_t _func;
    uint16_t _port;
    int _listen_sockfd;
};